package com.deng.eventflow.core.functions;

import java.util.ArrayList;
import java.util.List;

public class CumulativeFunction <T> extends UnaryFunction<T, T> {
    /**
     * The last value returned by the function
     */
    private T m_lastValue;

    /**
     * The stateless binary function to apply on each call
     */
    private BinaryFunction<T, T, T> m_function;

    /**
     * Instantiates a new cumulative function
     * @param function The function to cumulate
     */
    public CumulativeFunction(BinaryFunction<T, T, T> function)
    {
        super(function.getInputTypeLeft(), function.getOutputType());
        m_function = function;
        m_lastValue = m_function.getStartValue();
    }

    @Override
    public T getValue(T x)
    {
        if (m_lastValue == null)
        {
            // If the function did not provide a start value, use the
            // first given argument as the start value
            m_lastValue = x;
            return x;
        }
        T value = m_function.getValue(m_lastValue, x);
        m_lastValue = value;
        return value;
    }

    @Override
    public void reset()
    {
        m_lastValue = m_function.getStartValue();
    }

    @Override
    public CumulativeFunction<T> duplicate(boolean with_state)
    {
        CumulativeFunction<T> cf = new CumulativeFunction<T>(m_function.duplicate(with_state));
        if (with_state)
        {
            cf.m_lastValue = m_lastValue;
        }
        return cf;
    }

    /**
     * @since 0.10.2
     */
    @Override
    public Object printState()
    {
        List<Object> list = new ArrayList<Object>(2);
        list.add(m_function);
        list.add(m_lastValue);
        return list;
    }

    /**
     * @since 0.10.2
     */
    @SuppressWarnings("unchecked")
    public CumulativeFunction<T> readState(Object o)
    {
        List<Object> list = (List<Object>) o;
        BinaryFunction<T,T,T> func = (BinaryFunction<T,T,T>) list.get(0);
        CumulativeFunction<T> cf = new CumulativeFunction<T>(func);
        cf.m_lastValue = (T) list.get(1);
        return cf;
    }

    /**
     * @since 0.11
     * @return The last value
     */
    /*@ pure @*/ public T getLastValue()
    {
        return m_lastValue;
    }

}
